package com.sinopec.basemodule.http.api;

import com.jakewharton.retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import com.orhanobut.hawk.Hawk;
import com.sinopec.basemodule.AppConstants;
import com.sinopec.basemodule.BaseApplication;
import com.sinopec.basemodule.BuildConfig;
import com.sinopec.basemodule.common.Logger;
import com.sinopec.basemodule.http.dns.OkHttpDns;
import com.sinopec.basemodule.https.certification.TrustAllCerts;
import com.sinopec.basemodule.utils.NetUtil;

import java.io.File;
import java.net.URLDecoder;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSocketFactory;
import javax.net.ssl.TrustManager;
import javax.net.ssl.X509TrustManager;

import okhttp3.Cache;
import okhttp3.CacheControl;
import okhttp3.CipherSuite;
import okhttp3.ConnectionSpec;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.TlsVersion;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.converter.gson.GsonConverterFactory;

/**
 * lwj
 */
public class RetroAPIFactory {

    //设缓存有效期为1天
    public static final long CACHE_STALE_SEC = 60 * 60 * 24 * 1;
    //查询缓存的Cache-Control设置，为if-only-cache时只查询缓存而不会请求服务器，max-stale可以配合设置缓存失效时间
    public static final String CACHE_CONTROL_CACHE = "only-if-cached, max-stale=" + CACHE_STALE_SEC;
    //查询网络的Cache-Control设置
    //(假如请求了服务器并在a时刻返回响应结果，则在max-age规定的秒数内，浏览器将不会发送对应的请求到服务器，数据由缓存直接返回)
    public static final String CACHE_CONTROL_NETWORK = "Cache-Control: public, max-age=3600";
    // 避免出现 HTTP 403 Forbidden，参考：http://stackoverflow.com/questions/13670692/403-forbidden-with-java-but-not-web-browser
    static final String AVOID_HTTP403_FORBIDDEN =
            "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.11 (KHTML, like Gecko) Chrome/23.0.1271.95 Safari/537.11";
    private static Retrofit innerRetrofit;
    private static Retrofit outerRetrofit;

    private RetroAPIFactory() {
        throw new AssertionError();
    }

    //private static String defaultUrl = "https://www.baidu.com/";
    //private static String defaultUrl = "http://10.247.170.224/wmservice/palletApi/";
    //private static String defaultUrl = "https://test-ycfixreader.yeloworld.cn/appapi/";

    //private static String defaultUrl = "http://10.137.102.138:6005/wmservice/palletApi/";
    private static String defaultUrl = "http://10.137.102.138:6005/wmservice/palletApi/";

    public static String BASEURL = BuildConfig.isDebug ? Hawk.get(AppConstants.HAWK_APP_HOST, defaultUrl) : BuildConfig.HostUrl;

    /**
     * 云端响应头拦截器，用来配置缓存策略
     * Dangerous interceptor that rewrites the server's cache-control header.
     */
    private static final Interceptor sRewriteCacheControlInterceptor = chain -> {
        int code = BaseApplication.getInstance().getVersionCode();
        Request request = chain.request().newBuilder()
                .addHeader("token", BaseApplication.getInstance().getToken())
                .addHeader("app-id", "sinopec-fixedreader") //app标识（如果只是一个app可以不传）
                .addHeader("platform", "Android")
                .addHeader("android-version", String.valueOf(code)).build();
        if (!NetUtil.isNetworkAvailable(BaseApplication.getInstance())) {
            request = request.newBuilder().cacheControl(CacheControl.FORCE_CACHE).build();
            Logger.e("no network");
        }
        Response originalResponse = chain.proceed(request);
        if (NetUtil.isNetworkAvailable(BaseApplication.getInstance())) {
            //获取返回header重的版本配置和token字段，并做相应的缓存
            String cacheControl = request.cacheControl().toString();
            Map<String, List<String>> stringListMap = originalResponse.headers().toMultimap();
            List<String> versionCode = stringListMap.get(AppConstants.UPDATE_VERSION_CODE);
            List<String> tokens = stringListMap.get(AppConstants.PARAM_TOKEN);
            if (tokens != null && tokens.size() > 0) {
                BaseApplication.getInstance().updateToken(tokens.get(0));
            }

//            if (versionCode != null && versionCode.size() > 0) {
//                Integer integer = Integer.valueOf(versionCode.get(0));
//                if (integer > BaseApplication.getInstance().getVersionCode()) {
//                    Beta.checkUpgrade(false, false);
//                }
//            }

            initUpdate(stringListMap);

            return originalResponse.newBuilder()
                    .header("Cache-Control", cacheControl)
                    .removeHeader("Pragma")
                    .build();
        } else {
            return originalResponse.newBuilder()
                    .header("Cache-Control", "public, " + CACHE_CONTROL_CACHE)
                    .removeHeader("Pragma")
                    .build();
        }
    };

    private static void initUpdate(Map<String, List<String>> stringListMap) {
        int needUpdateFlag = 0;
        try {
            if (!stringListMap.isEmpty() && null != stringListMap.get(AppConstants.APP_HEADER_NEED_UPDATE_FLAG)) {
                needUpdateFlag = Integer.parseInt(stringListMap.get(AppConstants.APP_HEADER_NEED_UPDATE_FLAG).get(0));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        String appUpdateContent = null;
        try {
            if (!stringListMap.isEmpty() && null != stringListMap.get(AppConstants.APP_HEADER_UPDATE_CONTENT)) {
                appUpdateContent = stringListMap.get(AppConstants.APP_HEADER_UPDATE_CONTENT).get(0);
                appUpdateContent = URLDecoder.decode(appUpdateContent, "UTF-8");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        int forceUpdate = 0;
        try {
            if (!stringListMap.isEmpty() && null != stringListMap.get(AppConstants.APP_HEADER_FORCE_UPDATE_FLAG)) {
                forceUpdate = Integer.parseInt(stringListMap.get(AppConstants.APP_HEADER_FORCE_UPDATE_FLAG).get(0));
            }
        } catch (NumberFormatException e) {
            e.printStackTrace();
        }

        int serviceVersion = 0;
        try {
            if (!stringListMap.isEmpty() && null != stringListMap.get(AppConstants.APP_HEADER_NEW_VERSION)) {
                serviceVersion = Integer.parseInt(stringListMap.get(AppConstants.APP_HEADER_NEW_VERSION).get(0));
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        String appDownloadUrl = null;
        try {
            if (!stringListMap.isEmpty() && null != stringListMap.get(AppConstants.APP_HEADER_TRAY_QRCODE)) {
                appDownloadUrl = stringListMap.get(AppConstants.APP_HEADER_TRAY_QRCODE).get(0);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        BaseApplication.getInstance().getAppUpdateBean().setNeedUpdate(1 == needUpdateFlag);
        BaseApplication.getInstance().getAppUpdateBean().setAppUpdateDesc(appUpdateContent);
        BaseApplication.getInstance().getAppUpdateBean().setForce(1 == forceUpdate);
        BaseApplication.getInstance().getAppUpdateBean().setVersionCode(serviceVersion);
        BaseApplication.getInstance().getAppUpdateBean().setAppDownloadUrl(appDownloadUrl);
        BaseApplication.getInstance().appUpdateEvent(false);
    }


    private static final Interceptor rewriteResponseInterceptorOffline = chain -> {
        Request request = chain.request();
        if (!NetUtil.isNetworkAvailable(BaseApplication.getInstance().getApplicationContext())) {
            request = request.newBuilder()
                    .removeHeader("Pragma")
                    .header("Cache-Control", "public, only-if-cached")
                    .build();
        }
        return chain.proceed(request);
    };

    private static final HttpLoggingInterceptor mResponseLogInterceptor = new HttpLoggingInterceptor(message -> {
        if (message.contains("http")) {
            Logger.d("网络请求", "\nsglei-net请求地址：" + message);
        } else if (message.contains(AppConstants.PARAM_SIGN)) {
            Logger.d("网络请求", "\nsglei-net请求报文：" + message);
        } else if (message.contains(AppConstants.PARAM_DATA)) {
            Logger.d("网络请求", "\nsglei-net响应报文：" + message);
        }
    });

    public static <T> T create(final Class<T> service) {
        return innerRetrofit.create(service);
    }

    /**
     * 初始化网络通信服务
     */
    public static void init() {
        //下面代码看https://github.com/square/okhttp/wiki/HTTPS
        //https用ConnectionSpec.MODERN_TLS，不是的就用ConnectionSpec.CLEARTEXT
        ConnectionSpec spec = null;
        if (BASEURL.startsWith("https")) {
            spec = new ConnectionSpec.Builder(ConnectionSpec.MODERN_TLS)
                    .tlsVersions(TlsVersion.TLS_1_3,
                            TlsVersion.TLS_1_2)
                    .cipherSuites(
                            // TLSv1.3.
                            CipherSuite.TLS_AES_128_GCM_SHA256,
                            CipherSuite.TLS_AES_256_GCM_SHA384,
                            CipherSuite.TLS_CHACHA20_POLY1305_SHA256,

                            // TLSv1.0, TLSv1.1, TLSv1.2.
                            CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
                            CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
                            CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
                            CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
                            CipherSuite.TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
                            CipherSuite.TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,

                            // Note that the following cipher suites are all on HTTP/2's bad cipher suites list. We'll
                            // continue to include them until better suites are commonly available.
                            CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
                            CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
                            CipherSuite.TLS_RSA_WITH_AES_128_GCM_SHA256,
                            CipherSuite.TLS_RSA_WITH_AES_256_GCM_SHA384,
                            CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
                            CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
                            CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA)
                    .build();
        } else {
            spec = new ConnectionSpec.Builder(ConnectionSpec.CLEARTEXT)
                    .build();
        }

        //用来解决https需要证书认证的问题
        SSLSocketFactory ssfFactory;
        X509TrustManager trustManager;
        try {
            SSLContext sc = SSLContext.getInstance("TLS");
            trustManager = new TrustAllCerts();
            sc.init(null, new TrustManager[]{trustManager}, null);
            ssfFactory = sc.getSocketFactory();
        } catch (Exception e) {
            RuntimeException runtimeException = new RuntimeException("证书认证异常！");
            throw runtimeException;
        }

        // 指定缓存路径,缓存大小100Mb
        Cache cache = new Cache(new File(BaseApplication.getInstance().getCacheDir(), "HttpCache"),
                1024 * 1024 * 100);
        mResponseLogInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .dns(OkHttpDns.getInstance())
                .cache(cache)
                .retryOnConnectionFailure(true)
                .addInterceptor(rewriteResponseInterceptorOffline)
                .addInterceptor(mResponseLogInterceptor)
                .addNetworkInterceptor(sRewriteCacheControlInterceptor)
                .connectTimeout(30, TimeUnit.SECONDS)
                .readTimeout(30, TimeUnit.SECONDS)
                .writeTimeout(30, TimeUnit.SECONDS)
                .sslSocketFactory(ssfFactory, trustManager)
                .connectionSpecs(Collections.singletonList(spec))
//                .proxy(new Proxy(Proxy.Type.HTTP,new InetSocketAddress("10.0.71.33",8888)))
                //测试人员电脑ip
                //wr
                //.proxy(new Proxy(Proxy.Type.HTTP,new InetSocketAddress("10.0.70.97",8888)))
                //lt
                //.proxy(new Proxy(Proxy.Type.HTTP,new InetSocketAddress("10.0.71.134",8888)))
                .hostnameVerifier((s, sslSession) -> {
                    return true;
//                        return "test.image.tankchaoren.com".equals(s)
//                                || "test.carrierapi.tankchaoren.com".equals(s);
                })
                .build();

        innerRetrofit = new Retrofit.Builder()
                .client(okHttpClient)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .baseUrl(BASEURL)
                .build();

        outerRetrofit = new Retrofit.Builder()
                .client(okHttpClient)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .baseUrl(BASEURL)
                .build();
    }
}
